package main

import (
	"crypto/sha256"
	"errors"
	"fmt"
	"log"
	"net/url"
	"strings"
	"time"
)
const (
	tokenPrefix  = "K10"
	tokenFormat  = "%s%s::%s:%s"
	caHashLength = sha256.Size * 2

	defaultClientTimeout = 10 * time.Second
)
type Info struct {
	CACerts  []byte `json:"cacerts,omitempty"`
	BaseURL  string `json:"baseurl,omitempty"`
	Username string `json:"username,omitempty"`
	Password string `json:"password,omitempty"`
	caHash   string
}


func ParseAndValidateTokenForUser(server, token, username string) (*Info, error) {
	info, err := parseToken(token)
	if err != nil {
		return nil, err
	}

	info.Username = username

	if err := info.setAndValidateServer(server); err != nil {
		return nil, err
	}

	return info, nil
}

// parseToken parses a token into an Info struct
func parseToken(token string) (*Info, error) {
	var info Info

	if len(token) == 0 {
		return nil, errors.New("token must not be empty")
	}

	if !strings.HasPrefix(token, tokenPrefix) {
		token = fmt.Sprintf(tokenFormat, tokenPrefix, "", "", token)
	}

	// Strip off the prefix
	token = token[len(tokenPrefix):]

	parts := strings.SplitN(token, "::", 2)
	token = parts[0]
	if len(parts) > 1 {
		hashLen := len(parts[0])
		if hashLen > 0 && hashLen != caHashLength {
			return nil, errors.New("invalid token CA hash length")
		}
		info.caHash = parts[0]
		token = parts[1]
	}

	parts = strings.SplitN(token, ":", 2)
	if len(parts) != 2 || len(parts[1]) == 0 {
		return nil, errors.New("invalid token format")
	}

	info.Username = parts[0]
	info.Password = parts[1]

	return &info, nil
}

// setAndValidateServer updates the remote server's cert info, and validates it against the provided hash
func (i *Info) setAndValidateServer(server string) error {
	if err := i.setServer(server); err != nil {
		return err
	}
	return i.validateCAHash()
}


// setServer sets the BaseURL and CACerts fields of the Info by connecting to the server
// and storing the CA bundle.
func (i *Info) setServer(server string) error {
	url, err := url.Parse(server)
	if err != nil {
		log.Println("Invalid server url, failed to parse: ", server)
		return err
	}

	if url.Scheme != "https" {
		return errors.New("only https:// URLs are supported, invalid scheme: " + server)
	}

	for strings.HasSuffix(url.Path, "/") {
		url.Path = url.Path[:len(url.Path)-1]
	}

	if err != nil {
		return err
	}

	i.BaseURL = url.String()
	i.CACerts = []byte{'a','b'}
	return nil
}
// ValidateCAHash validates that info's caHash matches the CACerts hash.
func (i *Info) validateCAHash() error {
	if len(i.caHash) > 0 && len(i.CACerts) == 0 {
		// Warn if the user provided a CA hash but we're not going to validate because it's already trusted
		log.Println("Cluster CA certificate is trusted by the host CA bundle. " +
			"Token CA hash will not be validated.")
	} else if len(i.caHash) == 0 && len(i.CACerts) > 0 {
		// Warn if the CA is self-signed but the user didn't provide a hash to validate it against
		log.Println("Cluster CA certificate is not trusted by the host CA bundle, but the token does not include a CA hash. " +
			"Use the full token from the server's node-token file to enable Cluster CA validation.")
	} else if len(i.CACerts) > 0 && len(i.caHash) > 0 {
		log.Println("ok")
	}
	return nil
}

func main() {
	x,_ := parseToken("edge")
	fmt.Printf("url=%s, username=%s, passwd = %s, hash = %s\n",
		x.BaseURL, x.Username, x.Password, x.caHash)
}
